interface resultType {
  // 临时某一行关闭校验
  // eslint-disable-next-line
  [propName: string | symbol]: object;
}

//返回传递给他的任意对象的类
export const isClass: (o: unknown) => string = (o) => {
  if (o === null) return "Null";
  if (o === undefined) return "Undefined";
  // slice(start, end) 方法可提取字符串的某个部分，并以新的字符串返回被提取的部分。
  // 使用 start（包含） 和 end（不包含） 参数来指定字符串提取的部分。
  // start 参数字符串中第一个字符位置为 0, 第二个字符位置为 1, 以此类推，如果是负数表示从尾部截取多少个字符串，slice(-2) 表示提取原数组中的倒数第二个元素到最后一个元素（包含最后一个元素）。
  // end 参数如果为负数，-1 指字符串的最后一个字符的位置，-2 指倒数第二个字符，以此类推。
  // console.log(Object.prototype.toString.call(o).slice(8, -1));
  return Object.prototype.toString.call(o).slice(8, -1);
};
export const isSymbol = function (value: unknown): value is symbol {
  return typeof value === "symbol";
};

function isValidKey(
  key: string | number | symbol,
  tobject: any
): key is keyof typeof tobject {
  // console.log(key in object);
  return key in tobject;
}

export const isNullOrUndefined = (
  value: unknown
): value is null | undefined => {
  let result = false;
  if (value === null || value === undefined) {
    result = true;
  }
  return result;
};


//深度克隆
export const deepClone: (obj: object) => object = (obj) => {
  let result: resultType | object[];
  const oClass: string = isClass(obj);
  if (obj === undefined || obj === null) {
    return obj;
  }
  // obj 是Object对象
  if (oClass === "Object") {
    result = {};
  } else if (oClass === "Array") {
    // obj 是Array
    result = [];
  } else {
    return obj;
  }
  ////////////////////////////////////////
  // 拷贝自身
  // Reflect.ownKeys返回一个数组，包含对象自身的（不含继承的）所有键名，不管键名是 Symbol 或字符串，也不管是否可枚举。
  for (const key of Reflect.ownKeys(obj)) {
    if (isSymbol(key)) {
      if (
        key.toString() == "Symbol(Symbol.iterator)" ||
        key.toString() == "Symbol(Symbol.unscopables)"
      )
        continue;
    }
    let copy: object = {};
    if (isValidKey(key, obj)) {
      copy = obj[key];
    }
    // const copy: any = obj[key];
    if (isClass(copy) == "Object") {
      if (Array.isArray(result)) {
        result.push(deepClone(copy));
      } //递归调用
      else {
        result[key] = deepClone(copy);
      }
    } else if (isClass(copy) == "Array") {
      if (Array.isArray(result)) {
        result.push(deepClone(copy));
      } //递归调用
      else {
        result[key] = deepClone(copy);
      }
    } else {
      if (isNullOrUndefined(copy)) {
        Reflect.defineProperty(result, key, {
          value: copy,
          writable: true,
          enumerable: true,
          configurable: true,
        });
      } else {
        const objopd = Reflect.getOwnPropertyDescriptor(obj, key);
        if (objopd != undefined) {
          Reflect.defineProperty(result, key, objopd);
        }
      }
    }
  }
  ////////////////////////////////////////////////////////////////////////
  //   //拷贝原型的属性和方法-》 Object.keys返回一个数组，包括对象自身的（不含继承的）所有可枚举属性（不含 Symbol 属性）的键名。
  //   const protoObj: any = Reflect.getPrototypeOf(obj);
  const tPrototype: object | null = Reflect.getPrototypeOf(obj);
  if (tPrototype === null || tPrototype === undefined) {
    Reflect.setPrototypeOf(result, null);
  } else {
    const protoObj: object = tPrototype;
    if (protoObj == Array.prototype) {
      // 防止出现类数组对象
      Reflect.setPrototypeOf(result, Array.prototype);
    } else {
      Reflect.setPrototypeOf(result, {});
    }
    const resultProtoObj: resultType | object[] | object | null =
      Reflect.getPrototypeOf(result);
    if (resultProtoObj != null) {
      //拷贝原型-》属性和方法
      if (protoObj != null && protoObj != undefined) {
        for (const key of Reflect.ownKeys(protoObj)) {
          // const copy = protoObj[key];
          if (isSymbol(key)) {
            // console.log(key.toString());
            if (
              key.toString() == "Symbol(Symbol.iterator)" ||
              key.toString() == "Symbol(Symbol.unscopables)"
            )
              continue;
          }
          // const copy = protoObj[key] || obj[key]; // 防止class中的getter setter 原型无法读取值，所以要从实例属性上取下值
          let copy: object = {};
          if (isValidKey(key, protoObj)) {
            copy = protoObj[key];
            if (!copy) {
              if (isValidKey(key, obj)) {
                copy = obj[key];
              }
            }
          }
          if (isClass(copy) == "Object") {
            // resultProtoObj[key] = arguments.callee(copy); //递归调用
            if (Array.isArray(resultProtoObj)) {
              resultProtoObj.push(deepClone(copy));
            } //递归调用
            else {
              (<resultType>resultProtoObj)[key] = deepClone(copy); //递归调用
            }
          } else if (isClass(copy) == "Array") {
            // resultProtoObj[key] = arguments.callee(copy);
            if (Array.isArray(resultProtoObj)) {
              resultProtoObj.push(deepClone(copy));
            } //递归调用
            else {
              (<resultType>resultProtoObj)[key] = deepClone(copy); //递归调用
            }
          } else {
            // resultProtoObj[key] = copy;
            // console.log("bbbb__" + isClass(copy));
            // console.log(key);
            if (isNullOrUndefined(copy)) {
              Reflect.defineProperty(resultProtoObj, key, {
                value: copy,
                writable: true,
                enumerable: true,
                configurable: true,
              });
            } else {
              const protoObjopd = Reflect.getOwnPropertyDescriptor(
                protoObj,
                key
              );
              if (protoObjopd != undefined) {
                Reflect.defineProperty(resultProtoObj, key, protoObjopd);
              }
            }
          }
        }
      }
    }
  }
  ////////////////////////////////////////////////////////////////////////
  return result;
};

let parentage = Symbol("parentage");
let childage = Symbol("childage");
let obj_parent = {
  "parentname": "szq",
  parentArray:[2,4,6,{"c":"8","d":10}],
  parentFun:(y:number)=>{y*4},
  [parentage]: 44,
  get parent_age() { return this[parentage]; },
  set parent_age(value: number) { this[parentage] = value; },
};

let obj_child = {
  "childname": "syh",
  childArray:[1,2,3,{"a":"4","b":5}],
  childFun:(x:number)=>{x*2},
  [childage]: 13,
  get child_age() {
    return this[childage];
  },
  set child_age(value: number) {
    this[childage] = value;
  },
};
// 此处不能用assign方法合并两个类，会忽略访问器属性 ，直接变成普通属性
Object.setPrototypeOf(obj_child,obj_parent);



// console.log(obj_child.__proto__ === obj_parent)

let obj_copy = deepClone(obj_child);

let age0=obj_child.child_age;
obj_child.child_age=230
